home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / BasicListUI.java < prev    next >
Text File  |  1998-06-30  |  37KB  |  1,108 lines

  1. /*
  2.  * @(#)BasicListUI.java    1.28 98/04/14
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.*;
  24. import com.sun.java.swing.event.*;
  25. import com.sun.java.swing.plaf.*;
  26.  
  27. import java.awt.*;
  28. import java.awt.event.*;
  29.  
  30. import java.beans.PropertyChangeListener;
  31. import java.beans.PropertyChangeEvent;
  32.  
  33. import java.io.Serializable;
  34.  
  35.  
  36. /**
  37.  * A Windows L&F implementation of ListUI.
  38.  * <p>
  39.  * Warning: serialized objects of this class will not be compatible with
  40.  * future swing releases.  The current serialization support is appropriate 
  41.  * for short term storage or RMI between Swing1.0 applications.  It will
  42.  * not be possible to load serialized Swing1.0 objects with future releases
  43.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  44.  * baseline for the serialized form of Swing objects.
  45.  *
  46.  * @version 1.28 04/14/98
  47.  * @author Hans Muller
  48.  */
  49. public class BasicListUI extends ListUI implements Serializable
  50. {
  51.     /**
  52.      * True if the JList has the focus.  We cache this value rather than 
  53.      * relying on JComponent.hasFocus() as it's consulted per cell at 
  54.      * paint time.
  55.      * 
  56.      * #see InputListener
  57.      */
  58.     protected transient boolean hasFocus = false;
  59.  
  60.     protected ListSelectionListener selectionL;
  61.     protected ListDataListener dataL;
  62.     protected PropertyChangeListener propertyL;
  63.     protected InputListener inputL;
  64.  
  65.     protected JList list = null;
  66.     protected CellRendererPane rendererPane;
  67.  
  68.     // PENDING(hmuller) need a doc pointer to #getRowHeight, #maybeUpdateLayout
  69.     protected int[] cellHeights = null;
  70.     protected int cellHeight = -1;
  71.     protected int cellWidth = -1;
  72.     protected int updateLayoutStateNeeded = modelChanged;
  73.  
  74.     /* The bits below define JList property changes that affect layout.  
  75.      * When one of these properties changes we set a bit in 
  76.      * updateLayoutStateNeeded.  The change is dealt with lazily, see
  77.      * maybeUpdateLayout.  Changes to the JLists model, e.g. the
  78.      * models length changed, are handled similarly, see DataListener.
  79.      */
  80.  
  81.     protected final static int modelChanged = 1 << 0;
  82.     protected final static int selectionModelChanged = 1 << 1;
  83.     protected final static int fontChanged = 1 << 2;
  84.     protected final static int fixedCellWidthChanged = 1 << 3;
  85.     protected final static int fixedCellHeightChanged = 1 << 4;
  86.     protected final static int prototypeCellValueChanged = 1 << 5;
  87.     protected final static int cellRendererChanged = 1 << 6;
  88.  
  89.  
  90.     /**
  91.      * Paint one List cell: compute the relevant state, get the "rubber stamp"
  92.      * cell renderer component, and then use the CellRendererPane to paint it.
  93.      * Subclasses may want to override this method rather than paint().
  94.      * 
  95.      * @see #paint
  96.      */
  97.     protected void paintCell(
  98.         Graphics g, 
  99.         int row, 
  100.         Rectangle rowBounds, 
  101.         ListCellRenderer cellRenderer,
  102.         ListModel dataModel, 
  103.         ListSelectionModel selModel,
  104.         int leadIndex)
  105.     {
  106.         Object value = dataModel.getElementAt(row);
  107.         boolean cellHasFocus = hasFocus && (row == leadIndex);
  108.         boolean isSelected = selModel.isSelectedIndex(row);
  109.  
  110.         Component rendererComponent = 
  111.             cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
  112.                
  113.         int cx = rowBounds.x;
  114.         int cy = rowBounds.y;
  115.         int cw = rowBounds.width;
  116.         int ch = rowBounds.height;
  117.         rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true);
  118.     }
  119.  
  120.  
  121.     /**
  122.      * Paint the rows that intersect the Graphics objects clipRect.  This
  123.      * method calls paintCell as necessary.  Subclasses
  124.      * may want to override these methods.
  125.      * 
  126.      * @see #paintCell
  127.      */
  128.     public void paint(Graphics g, JComponent c) 
  129.     {
  130.         maybeUpdateLayoutState();
  131.  
  132.         ListCellRenderer renderer = list.getCellRenderer();
  133.         ListModel dataModel = list.getModel();
  134.         ListSelectionModel selModel = list.getSelectionModel();
  135.  
  136.         if ((renderer == null) || (dataModel.getSize() == 0)) {
  137.             return;
  138.         }
  139.  
  140.         /* Compute the area we're going to paint in terms of the affected
  141.          * rows (firstPaintRow, lastPaintRow), and the clip bounds.
  142.          */
  143.            
  144.         Rectangle paintBounds = g.getClipBounds();
  145.         int firstPaintRow = convertYToRow(paintBounds.y);
  146.         int lastPaintRow = convertYToRow((paintBounds.y + paintBounds.height) - 1);
  147.  
  148.         if (firstPaintRow == -1) {
  149.             firstPaintRow = 0;
  150.         }
  151.         if (lastPaintRow == -1) {
  152.             lastPaintRow = dataModel.getSize() - 1;
  153.         }
  154.  
  155.         Rectangle rowBounds = getCellBounds(list, firstPaintRow, firstPaintRow);
  156.         if (rowBounds == null) {
  157.             return;
  158.         }
  159.  
  160.         int leadIndex = list.getLeadSelectionIndex();
  161.  
  162.         for(int row = firstPaintRow; row <= lastPaintRow; row++) {
  163.             rowBounds.height = getRowHeight(row);
  164.  
  165.             /* Set the clip rect to be the intersection of rowBounds 
  166.              * and paintBounds and then paint the cell.
  167.              */
  168.  
  169.             g.setClip(rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height);
  170.             g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width, paintBounds.height);
  171.  
  172.             paintCell(g, row, rowBounds, renderer, dataModel, selModel, leadIndex);
  173.  
  174.             rowBounds.y += rowBounds.height;
  175.         }
  176.     }
  177.  
  178.  
  179.     /**
  180.      * The preferredSize of a list is total height of the rows 
  181.      * and the maximum width of the cells.  If JList.fixedCellHeight
  182.      * is specified then the total height of the rows is just
  183.      * (cellVerticalMargins + fixedCellHeight) * model.getSize() where
  184.      * rowVerticalMargins is the space we allocate for drawing 
  185.      * the yellow focus outline.  Similarly if JListfixedCellWidth is 
  186.      * specified then we just use that plus the horizontal margins.
  187.      * 
  188.      * @param c The JList component.
  189.      * @return The total size of the list.
  190.      */
  191.     public Dimension getPreferredSize(JComponent c) {
  192.         maybeUpdateLayoutState();
  193.         
  194.         int lastRow = list.getModel().getSize() - 1;
  195.         if (lastRow < 0) {
  196.             return new Dimension(0, 0);
  197.         }
  198.  
  199.         Insets insets = list.getInsets();
  200.  
  201.         int width = cellWidth + insets.left + insets.right;
  202.         int height = convertRowToY(lastRow) + getRowHeight(lastRow) + insets.bottom;
  203.         return new Dimension(width, height);
  204.     }
  205.  
  206.  
  207.     /**
  208.      * @returns The preferred size.
  209.      * @see #getPreferredSize
  210.      */
  211.     public Dimension getMinimumSize(JComponent c) {
  212.         return getPreferredSize(c);
  213.     }
  214.  
  215.  
  216.     /**
  217.      * @returns The preferred size.
  218.      * @see #getPreferredSize
  219.      */
  220.     public Dimension getMaximumSize(JComponent c) {
  221.         return getPreferredSize(c);
  222.     }
  223.  
  224.     
  225.     /**
  226.      * Selected the previous row and force it to be visible.
  227.      * Called by the KeyEvent.VK_UP keyboard action.
  228.      * 
  229.      * @see #registerKeyboardActions
  230.      * @see JList#ensureIndexIsVisible
  231.      */
  232.     protected void selectPreviousIndex() {
  233.         int s = list.getSelectedIndex();
  234.         if(s > 0) {
  235.             s -= 1;
  236.             list.setSelectedIndex(s);
  237.             list.ensureIndexIsVisible(s);
  238.         }
  239.     }
  240.  
  241.  
  242.     /**
  243.      * Selected the previous row and force it to be visible.
  244.      * Called by the KeyEvent.VK_DOWN keyboard action.
  245.      * 
  246.      * @see #registerKeyboardActions
  247.      * @see JList#ensureIndexIsVisible
  248.      */
  249.     protected void selectNextIndex() 
  250.     {
  251.         int s = list.getSelectedIndex();
  252.         if((s + 1) < list.getModel().getSize()) {
  253.             s += 1;
  254.             list.setSelectedIndex(s);
  255.             list.ensureIndexIsVisible(s);
  256.         }
  257.     }
  258.  
  259.  
  260.     /**
  261.      * Register keyboard actions for the up and down arrow keys.  The 
  262.      * actions just call out to protected methods, subclasses that 
  263.      * want to override or extend keyboard behavior should consider
  264.      * just overriding those methods.  This method is called at 
  265.      * installUI() time.
  266.      * 
  267.      * @see #selectPreviousIndex
  268.      * @see #selectNextIndex
  269.      * @see #installUI
  270.      */
  271.     protected void registerKeyboardActions()
  272.     {
  273.         /* SelectPreviousRow - UpArrow Key
  274.          */
  275.         {
  276.             AbstractAction action = new AbstractAction("SelectPreviousRow") {
  277.                 public void actionPerformed(ActionEvent e) { selectPreviousIndex(); }
  278.                 public boolean isEnabled() { return true; }
  279.             };
  280.             KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
  281.             list.registerKeyboardAction(action, key, JComponent.WHEN_FOCUSED);
  282.         }
  283.  
  284.         /* SelectNextRow - DownArrow Key
  285.          */
  286.         {
  287.             AbstractAction action = new AbstractAction("SelectNextRow") {
  288.                 public void actionPerformed(ActionEvent e) { selectNextIndex(); }
  289.                 public boolean isEnabled() { return true; }
  290.             };
  291.             KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
  292.             list.registerKeyboardAction(action, key, JComponent.WHEN_FOCUSED);
  293.         }
  294.     }
  295.  
  296.  
  297.     /**
  298.      * Unregister keyboard actions for the up and down arrow keys.  
  299.      * This method is called at uninstallUI() time - subclassess should
  300.      * ensure that all of the keyboard actions registered at installUI
  301.      * time are removed here.
  302.      * 
  303.      * @see #selectPreviousIndex
  304.      * @see #selectNextIndex
  305.      * @see #installUI
  306.      */
  307.     protected void unregisterKeyboardActions() {
  308.         list.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0));
  309.         list.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0));
  310.     }
  311.  
  312.  
  313.     /**
  314.      * Create and install the listeners for the JList, its model, and its 
  315.      * selectionModel.  This method is called at installUI() time.
  316.      * 
  317.      * @see #installUI
  318.      * @see #removeListListeners
  319.      */
  320.     protected void addListListeners()
  321.     {
  322.         inputL = createInputListener();
  323.         list.addMouseListener(inputL);
  324.         list.addMouseMotionListener(inputL);
  325.  
  326.         /* When a property changes that effects layout we set
  327.          * updateLayoutStateNeeded to the appropriate code.  We also 
  328.          * add/remove List data model listeners when the "model" 
  329.          * property changes.
  330.          */
  331.         propertyL = createPropertyListener();
  332.         list.addPropertyChangeListener(propertyL);
  333.  
  334.         /* When a mouseDown event occurs have the list requestFocus().
  335.          * We watch the focusIn/Out events so that the yellow leadIndex
  336.          * hightlight can be painted or not as neccessary.
  337.          */
  338.         list.addFocusListener(inputL);
  339.  
  340.         dataL = createDataListener();
  341.         ListModel model = list.getModel();
  342.         if (model != null) {
  343.             model.addListDataListener(dataL);
  344.         }
  345.  
  346.         selectionL = createSelectionListener();
  347.         ListSelectionModel selectionModel = list.getSelectionModel();
  348.         if (selectionModel != null) {
  349.             selectionModel.addListSelectionListener(selectionL);
  350.         }
  351.     }
  352.  
  353.  
  354.     /**
  355.      * Remove the listeners for the JList, its model, and its 
  356.      * selectionModel.  All of the listener fields, are reset to 
  357.      * null here.  This method is called at uninstallUI() time,
  358.      * it should be kept in sync with addListListeners.
  359.      *
  360.      * @see #uninstallUI
  361.      * @see #addListListeners
  362.      */
  363.     protected void removeListListeners()
  364.     {
  365.         list.removeMouseListener(inputL);
  366.         list.removeMouseMotionListener(inputL);
  367.         list.removePropertyChangeListener(propertyL);
  368.         list.removeFocusListener(inputL);
  369.  
  370.         ListModel model = list.getModel();
  371.         if (model != null) {
  372.             model.removeListDataListener(dataL);
  373.         }
  374.  
  375.         ListSelectionModel selectionModel = list.getSelectionModel();
  376.         if (selectionModel != null) {
  377.             selectionModel.removeListSelectionListener(selectionL);
  378.         }
  379.  
  380.         SelectionListener selectionL = null;
  381.         DataListener dataL = null;
  382.         InputListener inputL = null;
  383.         PropertyListener propertyL = null;
  384.     }
  385.  
  386.  
  387.     /**
  388.      * Initialize JList properties, e.g. font, foreground, and background,
  389.      * and add the CellRendererPane.  The font, foreground, and background
  390.      * properties are only set if their current value is either null
  391.      * or a UIResource, other properties are set if the current
  392.      * value is null.
  393.      * 
  394.      * @see #unconfigureList
  395.      * @see #installUI
  396.      * @see CellRendererPane
  397.      */
  398.     protected void configureList() 
  399.     {
  400.         list.setLayout(null);
  401.  
  402.         LookAndFeel.installBorder(list, "List.border");
  403.  
  404.         LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font");      
  405.  
  406.         if (list.getCellRenderer() == null) {
  407.             list.setCellRenderer((ListCellRenderer)(UIManager.get("List.cellRenderer")));
  408.         }
  409.         
  410.         Color sbg = list.getSelectionBackground();
  411.         if (sbg == null || sbg instanceof UIResource) {
  412.             list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
  413.         }
  414.  
  415.         Color sfg = list.getSelectionForeground();
  416.         if (sfg == null || sfg instanceof UIResource) {
  417.             list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
  418.         } 
  419.  
  420.         rendererPane = new CellRendererPane();
  421.         list.add(rendererPane);
  422.     }
  423.  
  424.  
  425.     /**
  426.      * Set the JList properties that haven't been explicitly overriden to 
  427.      * null.  A property is considered overridden if its current value
  428.      * is not a UIResource.
  429.      * 
  430.      * @see #configureList
  431.      * @see #uninstallUI
  432.      * @see CellRendererPane
  433.      */
  434.     protected void unconfigureList() 
  435.     {
  436.         list.remove(rendererPane);
  437.         rendererPane = null;
  438.  
  439.         if (list.getCellRenderer() instanceof UIResource) {
  440.             list.setCellRenderer(null);
  441.         }
  442.     }
  443.  
  444.  
  445.     /**
  446.      * Initializes <code>this.list</code> by calling <code>configureList()</code>,
  447.      * <code>addListListeners()</code>, and <code>registerKeyboardActions()</code>
  448.      * in order.
  449.      * 
  450.      * @see #configureList
  451.      * @see #addListListeners
  452.      * @see #registerKeyboardActions
  453.      */
  454.     public void installUI(JComponent list) 
  455.     {
  456.         this.list = (JList)list;
  457.         configureList();
  458.         addListListeners();
  459.         registerKeyboardActions();
  460.     }
  461.  
  462.  
  463.     /**
  464.      * Uninitializes <code>this.list</code> by calling <code>removeListListeners()</code>,
  465.      * <code>unregisterKeyboardActions()</code>, and <code>unconfigureList()</code>
  466.      * in order.  Sets this.list to null.
  467.      * 
  468.      * @see #removeListListeners
  469.      * @see #unregisterKeyboardActions
  470.      * @see #unconfigureList
  471.      */
  472.     public void uninstallUI(JComponent c) 
  473.     {
  474.         removeListListeners();
  475.         unregisterKeyboardActions();
  476.         unconfigureList();
  477.         
  478.         cellWidth = cellHeight = -1;
  479.         cellHeights = null;
  480.         
  481.         this.list = null;
  482.     }
  483.  
  484.  
  485.     /**
  486.      * Returns a new instance of BasicListUI.  BasicListUI delegates are
  487.      * allocated one per JList.
  488.      * 
  489.      * @return A new ListUI implementation for the Windows look and feel.
  490.      */
  491.     public static ComponentUI createUI(JComponent list) {
  492.         return new BasicListUI();
  493.     }
  494.  
  495.  
  496.     /**
  497.      * @return The index of the cell at location, or -1.
  498.      * @see ListUI#locationToIndex
  499.      */
  500.     public int locationToIndex(JList list, Point location) {
  501.         maybeUpdateLayoutState();
  502.         return convertYToRow(location.y);
  503.     }
  504.  
  505.  
  506.     /**
  507.      * @return The origin of the index'th cell.
  508.      * @see ListUI#indexToLocation
  509.      */
  510.     public Point indexToLocation(JList list, int index) {
  511.         maybeUpdateLayoutState();
  512.         return new Point(0, convertRowToY(index));
  513.     }
  514.  
  515.  
  516.     /** 
  517.      * @return The bounds of the index'th cell.
  518.      * @see ListUI#getCellBounds
  519.      */
  520.     public Rectangle getCellBounds(JList list, int index1, int index2) {
  521.         maybeUpdateLayoutState();
  522.         
  523.         int minIndex = Math.min(index1, index2);
  524.         int maxIndex = Math.max(index1, index2);
  525.         int minY = convertRowToY(minIndex);
  526.         int maxY = convertRowToY(maxIndex);
  527.  
  528.         if ((minY == -1) || (maxY == -1)) {
  529.             return null;
  530.         }
  531.  
  532.         Insets insets = list.getInsets();
  533.         int x = insets.left;
  534.         int y = minY;
  535.         int w = list.getWidth() - (insets.left + insets.right);
  536.         int h = (maxY + getRowHeight(maxIndex)) - minY;
  537.         return new Rectangle(x, y, w, h);
  538.     }
  539.  
  540.  
  541.     // PENDING(hmuller) explain the cell geometry abstraction in 
  542.     // the getRowHeight javadoc
  543.  
  544.     /**
  545.      * Returns the height of the specified row based on the current layout.
  546.      * 
  547.      * @return The specified row height or -1 if row isn't valid. 
  548.      * @see #convertYToRow
  549.      * @see #convertRowToY
  550.      * @see #updateLayoutState
  551.      */
  552.     protected int getRowHeight(int row) 
  553.     {
  554.         if ((row < 0) || (row >= list.getModel().getSize())) {
  555.             return -1;
  556.         }
  557.     // PENDING(hmuller) hacked around off by one.  Maybe the >= above should be >
  558.         return (cellHeights == null) ? cellHeight : ((row < cellHeights.length) ? cellHeights[row] : -1);
  559.     }
  560.  
  561.  
  562.     /**
  563.      * Convert the JList relative coordinate to the row that contains it,
  564.      * based on the current layout.  If y0 doesn't fall within any row, 
  565.      * return -1.
  566.      * 
  567.      * @return The row that contains y0, or -1.
  568.      * @see #getRowHeight
  569.      * @see #updateLayoutState
  570.      */
  571.     protected int convertYToRow(int y0)
  572.     {
  573.         int nrows = list.getModel().getSize();
  574.         Insets insets = list.getInsets();
  575.  
  576.         if (cellHeights == null) {
  577.             int row = (cellHeight == 0) ? 0 : ((y0 - insets.top) / cellHeight);
  578.             return ((row < 0) || (row >= nrows)) ? -1 : row;
  579.         }
  580.         else {
  581.             int y = insets.top;
  582.             int row = 0;
  583.  
  584.             for(int i = 0; i < nrows; i++) {
  585.                 if ((y0 >= y) && (y0 < y + cellHeights[i])) {
  586.                     return row;
  587.                 }
  588.                 y += cellHeights[i];
  589.                 row += 1;
  590.             }
  591.             return -1;
  592.         }
  593.     }
  594.  
  595.  
  596.     /**
  597.      * Return the JList relative Y coordinate of the origin of the specified 
  598.      * row or -1 if row isn't valide.
  599.      * 
  600.      * @return The Y coordinate of the origin of row, or -1.
  601.      * @see #getRowHeight
  602.      * @see #updateLayoutState
  603.      */
  604.     protected int convertRowToY(int row) 
  605.     {
  606.         int nrows = list.getModel().getSize();
  607.         Insets insets = list.getInsets();
  608.  
  609.         if ((row < 0) || (row > nrows)) {
  610.             return -1;
  611.         }
  612.  
  613.         if (cellHeights == null) {
  614.             return insets.top + (cellHeight * row);
  615.         }
  616.         else {
  617.             int y = insets.top;
  618.             for(int i = 0; i < row; i++) {
  619.                 y += cellHeights[i];
  620.             }
  621.             return y;
  622.         }
  623.     }
  624.  
  625.  
  626.     /**
  627.      * If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset
  628.      * updateLayoutStateNeeded.  This method should be called by methods 
  629.      * before doing any computation based on the geometry of the list.
  630.      * For example it's the first call in paint() and getPreferredSize().
  631.      * 
  632.      * @see #updateLayoutState
  633.      */
  634.     protected void maybeUpdateLayoutState()
  635.     {
  636.         if (updateLayoutStateNeeded != 0) {
  637.             updateLayoutState();
  638.             updateLayoutStateNeeded = 0;
  639.         }
  640.     }
  641.  
  642.  
  643.     /**
  644.      * Recompute the value of cellHeight or cellHeights based 
  645.      * and cellWidth, based on the current font and the current 
  646.      * values of fixedCellWidth, fixedCellHeight, and prototypeCellValue.
  647.      * 
  648.      * @see #maybeUpdateLayoutState
  649.      */
  650.     protected void updateLayoutState()
  651.     {
  652.         /* If both JList fixedCellWidth and fixedCellHeight have been
  653.          * set, then initialize cellWidth and cellHeight, and set 
  654.          * cellHeights to null.
  655.          */
  656.            
  657.         int fixedCellHeight = list.getFixedCellHeight();
  658.         int fixedCellWidth = list.getFixedCellWidth();
  659.  
  660.         cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1;
  661.  
  662.         if (fixedCellHeight != -1) {
  663.             cellHeight = fixedCellHeight;
  664.             cellHeights = null;
  665.         }
  666.         else {
  667.             cellHeight = -1;
  668.             cellHeights = new int[list.getModel().getSize()];
  669.         }
  670.  
  671.         /* If either of  JList fixedCellWidth and fixedCellHeight haven't
  672.          * been set, then initialize cellWidth and cellHeights by 
  673.          * scanning through the entire model.  Note: if the renderer is
  674.          * null, we just set cellWidth and cellHeights[*] to zero, 
  675.          * if they're not set already.
  676.          */
  677.         
  678.         if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) {
  679.  
  680.             ListModel dataModel = list.getModel();
  681.             int dataModelSize = dataModel.getSize();
  682.             ListCellRenderer renderer = list.getCellRenderer();
  683.  
  684.             if (renderer != null) {
  685.                 for(int index = 0; index < dataModelSize; index++) {
  686.                     Object value = dataModel.getElementAt(index);
  687.                     Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
  688.                     rendererPane.add(c);
  689.                     Dimension cellSize = c.getPreferredSize();
  690.                     if (fixedCellWidth == -1) {
  691.                         cellWidth = Math.max(cellSize.width, cellWidth);
  692.                     }
  693.                     if (fixedCellHeight == -1) {
  694.                         cellHeights[index] = cellSize.height;
  695.                     }
  696.                 }
  697.             }
  698.             else {
  699.                 if (cellWidth == -1) {
  700.                     cellWidth = 0;
  701.                 }
  702.                 for(int index = 0; index < dataModelSize; index++) {
  703.                     cellHeights[index] = 0;
  704.                 }
  705.             }
  706.         }
  707.  
  708.         list.invalidate();
  709.     }
  710.  
  711.  
  712.     /**
  713.      * Mouse input, and focus handling for JList.  An instance of this
  714.      * class is added to the appropriate java.awt.Component lists
  715.      * at installUI() time.  Note keyboard input is handled with JComponent
  716.      * KeyboardActions, see registerKeyboardActions(). 
  717.      * <p>
  718.      * Warning: serialized objects of this class will not be compatible with
  719.      * future swing releases.  The current serialization support is appropriate
  720.      * for short term storage or RMI between Swing1.0 applications.  It will
  721.      * not be possible to load serialized Swing1.0 objects with future releases
  722.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  723.      * baseline for the serialized form of Swing objects.
  724.      *
  725.      * @see #createInputListener
  726.      * @see #registerKeyboardActions
  727.      * @see #installUI
  728.      */
  729.     protected class InputListener extends MouseAdapter
  730.         implements MouseMotionListener, FocusListener, Serializable
  731.     {
  732.         public void mousePressed(MouseEvent e)
  733.         {
  734.             int row = convertYToRow(e.getY());
  735.             if (row != -1) {
  736.                 list.setValueIsAdjusting(true);
  737.                 int anchorIndex = list.getAnchorSelectionIndex();
  738.                 if (e.isControlDown()) {
  739.                     if (list.isSelectedIndex(row)) {
  740.                         list.removeSelectionInterval(row, row);
  741.                     }
  742.                     else {
  743.                         list.addSelectionInterval(row, row);
  744.                     }
  745.                 }
  746.                 else if (e.isShiftDown() && (anchorIndex != -1)) {
  747.                     list.setSelectionInterval(anchorIndex, row);
  748.                 }
  749.                 else {
  750.                     list.setSelectionInterval(row, row);
  751.                 }
  752.             }
  753.             if (!hasFocus) {
  754.                 list.requestFocus();
  755.             }
  756.         }
  757.  
  758.         public void mouseDragged(MouseEvent e) {
  759.             if (e.isShiftDown() || e.isControlDown()) {
  760.                 return;
  761.             }
  762.             int row = convertYToRow(e.getY());
  763.             if (row != -1) {
  764.                 Rectangle cellBounds = getCellBounds(list, row, row);
  765.                 if (cellBounds != null) {
  766.                     list.scrollRectToVisible(cellBounds);
  767.                     list.setSelectionInterval(row, row);
  768.                 }
  769.             }
  770.         }
  771.  
  772.         public void mouseMoved(MouseEvent e) {
  773.         }
  774.  
  775.         public void mouseReleased(MouseEvent e) {
  776.             if (e.isShiftDown() || e.isControlDown()) {
  777.                 return;
  778.             }
  779.             int row = convertYToRow(e.getY());
  780.             if (row != -1) {
  781.                 list.setSelectionInterval(row, row);
  782.             }
  783.             list.setValueIsAdjusting(false);
  784.         }
  785.  
  786.         protected void repaintCellFocus()
  787.         {
  788.             int leadIndex = list.getLeadSelectionIndex();
  789.             if (leadIndex != -1) {
  790.                 Rectangle r = getCellBounds(list, leadIndex, leadIndex);
  791.                 if (r != null) {
  792.                     list.repaint(r.x, r.y, r.width, r.height);
  793.                 }
  794.             }
  795.         }
  796.  
  797.         /* The focusGained() focusLost() methods run when the JList
  798.          * focus changes.
  799.          */
  800.  
  801.         public void focusGained(FocusEvent e) {
  802.             hasFocus = true;
  803.             repaintCellFocus();
  804.         }
  805.  
  806.         public void focusLost(FocusEvent e) {
  807.             hasFocus = false;
  808.             repaintCellFocus();
  809.         }
  810.     }
  811.  
  812.  
  813.     /**
  814.      * Creates a delegate that implements MouseListener, MouseMotionListener, 
  815.      * and FocusListener.  The delegate is added to the corresponding 
  816.      * java.awt.Component listener lists at installUI() time. Subclasses can 
  817.      * override this method to return a custom InputListener, e.g.
  818.      * <pre>
  819.      * class MyListUI extends BasicListUI {
  820.      *    protected InputListener <b>createInputListener</b>() {
  821.      *        return new MyInputListener();
  822.      *    }
  823.      *    public class MyInputListener extends InputListener {
  824.      *        public void mouseMoved(MouseEvent e) {
  825.      *            // do some extra work when the mouse moves
  826.      *            super.mouseMoved(e);
  827.      *        }
  828.      *    }
  829.      * }
  830.      * </pre>
  831.      * 
  832.      * @see InputListener
  833.      * @see #installUI
  834.      */
  835.     protected InputListener createInputListener() {
  836.         return new InputListener();
  837.     }
  838.  
  839.  
  840.     /**
  841.      * The ListSelectionListener that's added to the JLists selection
  842.      * model at installUI time, and whenever the JList.selectionModel property 
  843.      * changes.  When the selection changes we repaint the affected rows.
  844.      * <p>
  845.      * Warning: serialized objects of this class will not be compatible with
  846.      * future swing releases.  The current serialization support is appropriate
  847.      * for short term storage or RMI between Swing1.0 applications.  It will
  848.      * not be possible to load serialized Swing1.0 objects with future releases
  849.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  850.      * baseline for the serialized form of Swing objects.
  851.      *
  852.      * @see #createSelectionListener
  853.      * @see #getCellBounds
  854.      * @see #installUI
  855.      */
  856.     public class SelectionListener implements ListSelectionListener, Serializable
  857.     {
  858.         public void valueChanged(ListSelectionEvent e)
  859.         {
  860.             maybeUpdateLayoutState();
  861.  
  862.             int minY = convertRowToY(e.getFirstIndex());
  863.             int maxY = convertRowToY(e.getLastIndex());
  864.  
  865.             if ((minY == -1) || (maxY == -1)) {
  866.                 list.repaint(0, 0, list.getWidth(), list.getHeight());
  867.             }
  868.             else {
  869.                 maxY += getRowHeight(e.getLastIndex());
  870.                 list.repaint(0, minY, list.getWidth(), maxY - minY);
  871.             }
  872.         }
  873.     }
  874.  
  875.  
  876.     /**
  877.      * Creates an instance of ListSelectionListener that's added to 
  878.      * the JLists by selectionModel as needed.  Subclasses can override 
  879.      * this method to return a custom ListSelectionListener, e.g.
  880.      * <pre>
  881.      * class MyListUI extends BasicListUI {
  882.      *    protected ListSelectionListener <b>createSelectionListener</b>() {
  883.      *        return new MySelectionListener();
  884.      *    }
  885.      *    public class MySelectionListener extends SelectionListener {
  886.      *        public void valueChanged(ListSelectionEvent e) {
  887.      *            // do some extra work when the selection changes
  888.      *            super.valueChange(e);
  889.      *        }
  890.      *    }
  891.      * }
  892.      * </pre>
  893.      * 
  894.      * @see SelectionListener
  895.      * @see #installUI
  896.      */
  897.     protected ListSelectionListener createSelectionListener() {
  898.         return new SelectionListener();
  899.     }
  900.  
  901.  
  902.     /**
  903.      * The ListDataListener that's added to the JLists model at
  904.      * installUI time, and whenever the JList.model property changes.
  905.      * <p>
  906.      * Warning: serialized objects of this class will not be compatible with
  907.      * future swing releases.  The current serialization support is appropriate
  908.      * for short term storage or RMI between Swing1.0 applications.  It will
  909.      * not be possible to load serialized Swing1.0 objects with future releases
  910.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  911.      * baseline for the serialized form of Swing objects.
  912.      *
  913.      * @see JList#getModel
  914.      * @see #maybeUpdateLayout
  915.      * @see #createDataListener
  916.      * @see #installUI
  917.      */
  918.     public class DataListener implements ListDataListener, Serializable
  919.     {
  920.         public void intervalAdded(ListDataEvent e) {
  921.             updateLayoutStateNeeded = modelChanged;
  922.  
  923.             int minIndex = Math.min(e.getIndex0(), e.getIndex1());
  924.             int maxIndex = Math.max(e.getIndex0(), e.getIndex1());
  925.  
  926.             /* Sync the SelectionModel with the DataModel.
  927.              */
  928.  
  929.             ListSelectionModel sm = list.getSelectionModel();
  930.             if (sm != null) {
  931.                 sm.insertIndexInterval(minIndex, maxIndex - minIndex, true);
  932.             }
  933.  
  934.             /* Repaint the entire list, from the origin of
  935.              * the first added cell, to the bottom of the
  936.              * component.
  937.              */
  938.  
  939.             int y = Math.max(0, convertRowToY(minIndex));
  940.             int h = list.getHeight() - y;
  941.             list.revalidate();
  942.             list.repaint(0, y, list.getWidth(), h);
  943.         }
  944.  
  945.  
  946.         public void intervalRemoved(ListDataEvent e) 
  947.         {
  948.             updateLayoutStateNeeded = modelChanged;
  949.  
  950.             /* Sync the SelectionModel with the DataModel.
  951.              */
  952.  
  953.             ListSelectionModel sm = list.getSelectionModel();
  954.             if (sm != null) {
  955.                 sm.removeIndexInterval(e.getIndex0(), e.getIndex1());
  956.             }
  957.  
  958.             /* Repaint the entire list, from the origin of
  959.              * the first removed cell, to the bottom of the
  960.              * component.
  961.              */
  962.  
  963.             int minIndex = Math.min(e.getIndex0(), e.getIndex1());
  964.             int y = Math.max(0, convertRowToY(minIndex));
  965.             int h = list.getHeight() - y;
  966.             list.revalidate();
  967.             list.repaint(0, y, list.getWidth(), h);
  968.         }
  969.  
  970.  
  971.         public void contentsChanged(ListDataEvent e) {
  972.             updateLayoutStateNeeded = modelChanged;
  973.             list.revalidate();
  974.             list.repaint();
  975.         }
  976.     }
  977.  
  978.  
  979.     /**
  980.      * Creates an instance of ListDataListener that's added to 
  981.      * the JLists by model as needed.  Subclasses can override 
  982.      * this method to return a custom ListDataListener, e.g.
  983.      * <pre>
  984.      * class MyListUI extends BasicListUI {
  985.      *    protected ListDataListener <b>createDataListener</b>() {
  986.      *        return new MyDataListener();
  987.      *    }
  988.      *    public class MyDataListener extends DataListener {
  989.      *        public void contentsChanged(ListDataEvent e) {
  990.      *            // do some extra work when the models contents change
  991.      *            super.contentsChange(e);
  992.      *        }
  993.      *    }
  994.      * }
  995.      * </pre>
  996.      * 
  997.      * @see DataListener
  998.      * @see JList#getModel
  999.      * @see #installUI
  1000.      */
  1001.     protected ListDataListener createDataListener() {
  1002.         return new DataListener();
  1003.     }
  1004.  
  1005.  
  1006.     /**
  1007.      * The PropertyChangeListener that's added to the JList at
  1008.      * installUI time.  When the value of a JList property that 
  1009.      * affects layout changes, we set a bit in updateLayoutStateNeeded.  
  1010.      * If the JLists model changes we additionally remove our listeners 
  1011.      * from the old model.  Likewise for the JList selectionModel.
  1012.      * <p>
  1013.      * Warning: serialized objects of this class will not be compatible with
  1014.      * future swing releases.  The current serialization support is appropriate
  1015.      * for short term storage or RMI between Swing1.0 applications.  It will
  1016.      * not be possible to load serialized Swing1.0 objects with future releases
  1017.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  1018.      * baseline for the serialized form of Swing objects.
  1019.      *
  1020.      * @see #maybeUpdateLayout
  1021.      * @see #createPropertyListener
  1022.      * @see #installUI
  1023.      */
  1024.     public class PropertyListener implements PropertyChangeListener, Serializable
  1025.     {
  1026.         public void propertyChange(PropertyChangeEvent e)
  1027.         {
  1028.             String propertyName = e.getPropertyName();
  1029.             
  1030.             /* If the JList.model property changes, remove our listener, 
  1031.              * dataL from the old model and add it to the new one.
  1032.              */
  1033.             if (propertyName.equals("model")) {
  1034.                 ListModel oldModel = (ListModel)e.getOldValue();
  1035.                 ListModel newModel = (ListModel)e.getNewValue();
  1036.                 if (oldModel != null) {
  1037.                     oldModel.removeListDataListener(dataL);
  1038.                 }
  1039.                 if (newModel != null) {
  1040.                     newModel.addListDataListener(dataL);
  1041.                 }
  1042.                 updateLayoutStateNeeded |= modelChanged;
  1043.             }
  1044.  
  1045.             /* If the JList.selectionModel property changes, remove our listener, 
  1046.              * selectionL from the old selectionModel and add it to the new one.
  1047.              */
  1048.             else if (propertyName.equals("selectionModel")) {
  1049.                 ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue();
  1050.                 ListSelectionModel newModel = (ListSelectionModel)e.getNewValue();
  1051.                 if (oldModel != null) {
  1052.                     oldModel.removeListSelectionListener(selectionL);
  1053.                 }
  1054.                 if (newModel != null) {
  1055.                     newModel.addListSelectionListener(selectionL);
  1056.                 }
  1057.                 updateLayoutStateNeeded |= modelChanged;
  1058.             }
  1059.             else if (propertyName.equals("cellRenderer")) {
  1060.                 updateLayoutStateNeeded |= cellRendererChanged;
  1061.             }
  1062.             else if (propertyName.equals("font")) {
  1063.                 updateLayoutStateNeeded |= fontChanged;
  1064.             }
  1065.             else if (propertyName.equals("prototypeCellValue")) {
  1066.                 updateLayoutStateNeeded |= prototypeCellValueChanged;
  1067.             }
  1068.             else if (propertyName.equals("fixedCellHeight")) {
  1069.                 updateLayoutStateNeeded |= fixedCellHeightChanged;
  1070.             }
  1071.             else if (propertyName.equals("fixedCellWidth")) {
  1072.                 updateLayoutStateNeeded |= fixedCellWidthChanged;
  1073.             }
  1074.             else if (propertyName.equals("cellRenderer")) {
  1075.                 updateLayoutStateNeeded |= cellRendererChanged;
  1076.             }
  1077.         }
  1078.     }
  1079.  
  1080.  
  1081.     /**
  1082.      * Creates an instance of PropertyChangeListener that's added to 
  1083.      * the JList by installUI().  Subclasses can override this method
  1084.      * to return a custom PropertyChangeListener, e.g.
  1085.      * <pre>
  1086.      * class MyListUI extends BasicListUI {
  1087.      *    protected PropertyChangeListener <b>createPropertyListener</b>() {
  1088.      *        return new MyPropertyListener();
  1089.      *    }
  1090.      *    public class MyPropertyListener extends PropertyListener {
  1091.      *        public void propertyChange(PropertyChangeEvent e) {
  1092.      *            if (e.getPropertyName().equals("model")) {
  1093.      *                // do some extra work when the model changes
  1094.      *            }
  1095.      *            super.propertyChange(e);
  1096.      *        }
  1097.      *    }
  1098.      * }
  1099.      * </pre>
  1100.      * 
  1101.      * @see PropertyListener
  1102.      * @see #installUI
  1103.      */
  1104.     protected PropertyChangeListener createPropertyListener() {
  1105.         return new PropertyListener();
  1106.     }
  1107. }
  1108.